﻿using System;
using System.Collections.Generic;

namespace PokerCards
{
    public class Hand
    {
        private CardHolder theHand;
        public Hand()
        {
            theHand = new CardHolder();
        }
        public void Add(Card card)
        {
            theHand.Add(card);
        }
        public void Clear()
        {
            theHand.Clear();
        }
        public PokerScore Score()
        {
            bool flush = HasFlush();
            bool straight = HasStraight();
            bool pairs = HasPairs();
            if (flush)
            {
                return GetFlushType(straight);
            }
            if (straight)
            {
                return PokerScore.Straight;
            }
            if (pairs)
            {
                return GetDuplicates();
            }
            return PokerScore.HighTwo;
        }
        private PokerScore GetFlushType(bool straight)
        {
            if (straight)
            {
                if (theHand.GetUniqueRankHashCode() == 0x1111100000000)
                    return PokerScore.RoyalFlush;
                else
                    return PokerScore.StraightFlush;
            }
            else
            {
                return PokerScore.Flush;
            }
        }
        private bool HasFlush()
        {
            var suit = theHand.GetSuitHashCode();
            bool spadeFlush =   ((0xF000 & suit) >= 0x5000);
            bool heartFlush =   ((0x0F00 & suit) >= 0x0500);
            bool diamondFlush = ((0x00F0 & suit) >= 0x0050);
            bool clubFlush =    ((0x000F & suit) >= 0x0005);
            return spadeFlush || heartFlush || diamondFlush || clubFlush;
        }
        private bool HasStraight()
        {
            var rank = theHand.GetUniqueRankHashCode();
            for (int i = 0; i < 9; i++)
            {
                bool s = ((getRankOffSet(rank, i) & 0x11111) == 0x11111);
                if (s)
                    return true;
            }

            return false;
        }
        private bool HasPairs()
        {
            int bitCount = SparseBitcount(theHand.GetUniqueRankHashCode());
            int cardCount = theHand.Count();
            if (bitCount < cardCount)
            {
                return true;
            }
            return false;
        }
        private PokerScore GetDuplicates()
        {
            ulong rank = theHand.GetRankHashCode();
            //check 4 of a kind
            if (quantityCheck(rank, 4))
                return PokerScore.FourOfKind;
            if (quantityCheck(rank, 3))
            {
                if (quantityCheck(rank, 2))
                    return PokerScore.FullHouse;
                else
                    return PokerScore.ThreeOfKind;
            }
            if (quantityCheck(rank, 2, 2))
            {
                return PokerScore.TwoPair;
            }
            return PokerScore.OnePair;
        }
        private bool quantityCheck(ulong rank, ulong quantity, int offset=1)
        {
            int cur = 0;
            for (int i = 0; i < 13; i++)
            {
                ulong check = getRankOffSet(rank, i) & 0xF;
                if (check == quantity)
                {
                    cur++;
                    if (offset==cur)
                        return true;
                }
            }
            return false;
        }
        private ulong getRankOffSet(ulong card, int i)
        {
            return (card >> (i * 4));
        }

        //public Card GetHighCard()
        //{

        //}
        private int SparseBitcount(ulong n)
        {
            int count = 0;
            while (n != 0)
            {
                count++;
                n &= (n - 1);
            }
            return count;
        }

        public void SortByRank()
        {
            throw new NotImplementedException();
        }
    }
}
